﻿
#include "httpclient.h"
#include "../../iocp/IOCP/httputils.h"
#include "../../iocp/IOCP/fileio.h"
#include <tchar.h>

using CBARG = struct __CBARG
{
    http_pathhandle_cb pfnHttpCallback;
    struct __HTTPINFO* pHttpInfo;
};

void http_signal_call(HIOCPFILE hSocket, DWORD dwEvent, DWORD dwError);

void http_listen_cb(struct __IOCPFILE* hListen, struct __IOCPFILE* hClient);
void http_read_cb(HIOCPFILE hObject, void* pUserData);
void http_write_cb(HIOCPFILE hObject, void* pUserData);
void* __stdcall websvr_thread_callback(void* param);

http_bool_t http_parser_first_row_callback(const char* strField1, int nField1,
                                           const char* strField2, int nField2, const char* strField3, int nField3,
                                           void* arg);

//如果field 和 value都是空, 说明http请求已经结束
//如果不希望继续之后后续的解析，请返回 FALSE，http_request_parser也会返回失败
http_bool_t http_parser_header_callback(const char* strField, int nField,
                                        const char* strValue, int nValue, void* arg);

PHTTPSERVER http_listen(HIOCPBASE hIocp)
{
    BOOL bListen = FALSE;

    SOCKADDR_IN SockAddr = {0};
    int nAddrSize = sizeof(SOCKADDR_IN);
    SockAddr.sin_family = AF_INET;
    SockAddr.sin_addr.s_addr = 0;

    HIOCPFILE hListen = fileio_create(hIocp, NULL, IOTYPE_TCP);
    fileio_set_name(hListen, TEXT("http_listener"));

    SockAddr.sin_port = htons(20080);
    bListen = fileio_listen(hListen, &SockAddr, http_listen_cb);
    if (bListen)
    {
        fileio_close(hListen, 0);
        return nullptr;
    }

    if (FALSE == bListen)
    {
        fileio_close(hListen, 0);
        return nullptr;
    }

    HIOCPFILE hAccept = fileio_create(hIocp, NULL, IOTYPE_TCP);
    fileio_set_name(hAccept, TEXT("http_accepter"));
    fileio_setcb(hAccept, http_read_cb, http_write_cb, http_signal_call, nullptr);
    fileio_accept(hListen, hAccept);

    auto pServer = new HTTPSERVER;

    pServer->hObject = hListen;
    fileio_set_userdata(hListen, pServer);
    return pServer;
}

void http_listen_close(PHTTPSERVER pHttpServer)
{
    if (nullptr == pHttpServer)
    {
        return;
    }

    if (pHttpServer->hObject)
    {
        fileio_close(pHttpServer->hObject, 0);
    }
    delete pHttpServer;
}

BOOL http_register_pathcall(PHTTPSERVER pHttpServer, const char* strPath, http_pathhandle_cb pfnPathHandle)
{
    if (!pHttpServer || !strPath || 0 == strPath[0] || !pfnPathHandle)
    {
        return FALSE;
    }
    std::string strPathName;

    if (strPath[0] != '/' && strPath[0] != '\\')
    {
        strPathName.append("/");
    }

    strPathName.append(strPath);

    for (int i = 0; strPathName.size() > i; i++)
    {
        if ('Z' >= strPathName[i] && strPathName[i] >= 'A')
        {
            strPathName[i] = strPathName[i] - ('Z' - 'z');
        }
        else if (strPathName[i] == '\\')
        {
            strPathName[i] = '/';
        }
    }

    if (strPathName[strPathName.size() - 1] != '/')
    {
        strPathName.append("/");
    }

    pHttpServer->PathHandles.insert(std::pair<std::string, http_pathhandle_cb>(strPathName, pfnPathHandle));

    return TRUE;
}

BOOL http_register_errorcall(PHTTPSERVER pHttpServer, http_pathhandle_cb pfnPathHandle)
{
    if (!pHttpServer || !pfnPathHandle)
    {
        return FALSE;
    }
    pHttpServer->ErrorHandle = pfnPathHandle;
}

void http_close(HTTPINFO* pHttp, DWORD dwSecond)
{
    if (nullptr == pHttp)
    {
        return;
    }

    HIOCPFILE hObject = pHttp->hHttp;

    free(pHttp);

    if (hObject)
    {
        fileio_set_userdata(hObject, nullptr);
        fileio_close(hObject, dwSecond);
    }
}

void http_listen_cb(struct __IOCPFILE* hListen, struct __IOCPFILE* hClient)
{
    auto pHttpInfo = new HTTPINFO;
    auto pServer = static_cast<PHTTPSERVER>(fileio_get_userdata(hListen));

    memset(pHttpInfo, 0, sizeof(HTTPINFO));
    pHttpInfo->hHttp = hClient;
    pHttpInfo->pHttpServer = pServer;
    fileio_set_userdata(hClient, pHttpInfo);
    fileio_enable_read(hClient, TRUE); //接收到新套接字后，对套接字开启读监听

    websvr_accept_complete_callback(pServer, pHttpInfo);

    HIOCPFILE hAccept = fileio_create(fileio_get_iobase(hListen), NULL, IOTYPE_TCP);
    fileio_set_name(hAccept, TEXT("http_accepter"));
    fileio_setcb(hAccept, http_read_cb, http_write_cb, http_signal_call, nullptr);
    fileio_accept(hListen, hAccept);
}

void http_signal_call(HIOCPFILE hObject, DWORD dwEvent, DWORD dwError) //错误通知
{
    if (0 == _tcsicmp(fileio_get_name(hObject), TEXT("http_accepter")))
    {
        auto pHttp = static_cast<HTTPINFO*>(fileio_get_userdata(hObject));
        if (pHttp)
        {
            delete pHttp;
            fileio_set_userdata(hObject, nullptr);
        }
    }
    fileio_close(hObject, 0);
}

BOOL http_check_fileend(HTTPINFO* pHttp)
{
    if (0 != pHttp->nContentLength && pHttp->nBufferOffset > pHttp->nCheckOffset + pHttp->nContentLength)
    {
        //http 超文件还没接收完毕
        return FALSE;
    }

    websvr_read_complete_callback(pHttp);
    return TRUE;
    //到这里就是接收完毕了，
}


void http_read_cb(HIOCPFILE hObject, void* pUserData)
{
    auto pHttp = static_cast<HTTPINFO*>(pUserData);
    int nOffset = pHttp->nBufferOffset;
    int nRet = 0;
    nRet = fileio_read(hObject, &pHttp->Buffer[nOffset], sizeof(pHttp->Buffer) - nOffset);
    pHttp->nBufferOffset += nRet;
    int nCheckOffset = pHttp->nCheckOffset;

    if (pHttp->bParser)
    {
        //已经解析过头部了
        http_check_fileend(pHttp);
        return;
    }

    int nCheck = http_check_eof(&pHttp->Buffer[nCheckOffset], &nCheckOffset);
    switch (nCheck)
    {
    case -1: //Http 头部文本格式有误
        {
            fileio_close(hObject, 0);
        }
        break;
    case 1: //头部接收完成
        {
            pHttp->nCheckOffset = nCheckOffset;
            nCheck = http_parser_execute(pHttp->Buffer, http_parser_first_row_callback, http_parser_header_callback,
                                         pHttp);

            if (pHttp->nCheckOffset != nCheck || pHttp->nCheckOffset + pHttp->nContentLength >= sizeof(pHttp->Buffer))
            {
                //缓冲不正确，或者缓冲过大
                fileio_close(hObject, 0);
                return;
            }

            pHttp->bParser = TRUE;
            http_check_fileend(pHttp);
        }
        break;
    case 0: //还没接收完成
    default:
        {
            pHttp->nCheckOffset = nCheckOffset;
        }
        break;
    }
}

void http_write_cb(HIOCPFILE hObject, void* pUserData)
{
    //printf("tcp_write_cb %d \n", hObject, fileio_get_output_lenght(hObject));
    auto pHttp = static_cast<HTTPINFO*>(pUserData);
    if (0 == fileio_get_output_lenght(hObject))
    {
        websvr_write_complete_callback(pHttp);
    }
}


http_bool_t http_parser_first_row_callback(const char* strField1, int nField1,
                                           const char* strField2, int nField2, const char* strField3, int nField3,
                                           void* arg)
{
    auto pHttp = static_cast<HTTPINFO*>(arg);

    if (sizeof(pHttp->strModle) > nField1)
    {
        strncpy(pHttp->strModle, strField1, nField1);
    }

    pHttp->strPath = strField2;
    //提取路径指针和长度
    while (nField2 > pHttp->nPathSize && '?' != strField2[pHttp->nPathSize])
    {
        pHttp->nPathSize++;
    }

    if (nField2 > pHttp->nPathSize && '?' == strField2[pHttp->nPathSize])
    {
        //存在参数
        pHttp->strParam = strField2 + pHttp->nPathSize + 1;
        pHttp->nParamSize = nField2 - pHttp->nPathSize - 1;
    }

    return TRUE;
}

http_bool_t http_parser_header_callback(const char* strField, int nField,
                                        const char* strValue, int nValue, void* arg)
{
    auto pHttp = static_cast<HTTPINFO*>(arg);
    char strTmp[64] = {0};
    if (0 == _strnicmp(strField, "content-length", nField))
    {
        strncpy(strTmp, strValue, nValue);
        pHttp->nContentLength = atoi(strTmp);
    }
    else if (0 == _strnicmp(strField, "Connection", nField))
    {
        if (0 == _strnicmp(strValue, "keep-alive", nValue))
        {
            //pHttp->bKeepAlive = TRUE; //暂不支持
        }
    }

    return TRUE;
}

void websvr_read_complete_callback(struct __HTTPINFO* pHttpInfo)
{
    struct __HTTPSERVER* pHttpServer = pHttpInfo->pHttpServer;

    if (!pHttpInfo || !pHttpServer)
    {
        return;
    }

    if (pHttpInfo->strPath)
    {
        std::string strPathName;
        if (pHttpInfo->strPath[0] != '/' && pHttpInfo->strPath[0] != '\\')
        {
            strPathName.append("/");
        }

        strPathName.append(pHttpInfo->strPath, pHttpInfo->nPathSize);

        for (int i = 0; strPathName.size() > i; i++)
        {
            if ('Z' >= strPathName[i] && strPathName[i] >= 'A')
            {
                strPathName[i] = strPathName[i] - ('Z' - 'z');
            }
            else if (strPathName[i] == '\\')
            {
                strPathName[i] = '/';
            }
        }

        if (strPathName[strPathName.size() - 1] != '/')
        {
            strPathName.append("/");
        }

        auto iter = pHttpServer->PathHandles.find(strPathName);
        if (iter != pHttpServer->PathHandles.end() && iter->second)
        {
            iter->second(pHttpInfo);
        }
        else 
        {
            pHttpServer->ErrorHandle(pHttpInfo);
        }
    }
}

void websvr_write_complete_callback(struct __HTTPINFO* pHttpInfo)
{
    if (FALSE == pHttpInfo->bKeepAlive)
    {
        http_close(pHttpInfo, HTTP_CLOSETIME);
    }
}

void websvr_accept_complete_callback(struct __HTTPSERVER* pHttpServer, struct __HTTPINFO* pHttpInfo)
{
}

void* __stdcall websvr_thread_callback(void* param)
{
    auto arg = static_cast<CBARG*>(param);
    arg->pfnHttpCallback(arg->pHttpInfo);
    fileio_decrement(arg->pHttpInfo->hHttp);
    return nullptr;
}
